package com.geline.mybatisplus.context;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.geline.mybatisplus.annotation.ExtractCount;
import com.geline.mybatisplus.annotation.ExtractKey;
import com.geline.mybatisplus.annotation.ExtractSum;
import com.geline.mybatisplus.annotation.ExtractTable;
import com.geline.mybatisplus.handler.ForeignKeyHandler;
import com.geline.mybatisplus.util.SpELParserUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanMap;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Objects;

/**
 * @author mx
 * @date 2025/9/26
 */
@Slf4j
public class ExtractTableManager {

    private ExtractTableThreadLocal threadLocal;
    private ForeignKeyHandler foreignKeyHandler;

    public ExtractTableManager(ExtractTableThreadLocal threadLocal, ForeignKeyHandler foreignKeyHandler){
        this.threadLocal = threadLocal;
        this.foreignKeyHandler = foreignKeyHandler;
    }

    public ExtractTableThreadLocal getThreadLocal() {
        return threadLocal;
    }

    public void handleChildEntity(Object entity){
        Class<?> entityClass = entity.getClass();
        TableName child = entity.getClass().getAnnotation(TableName.class);

        //处理主表(存在 @ExtractKey)
        ExtractTable[] array = entityClass.getAnnotationsByType(ExtractTable.class);
        if(array!=null && array.length>0){
            for (ExtractTable extractTable : array) {
                boolean autoFill = extractTable.autoFill();
                if(!autoFill){
                    continue;
                }
                Object fullEntity = getEntityById(entity);
                String remark = extractTable.remark();
                String condition = extractTable.condition();
                //存在属性值时才执行查询
                if(StrUtil.isNotBlank(condition)){
                    Boolean execute = SpELParserUtil.executeBoolean(fullEntity, condition);
                    log.debug("@ExtractTable[{} -> {}]({}, {})={}", entityClass.getSimpleName(), extractTable.value(), remark, condition, execute);
                    if(!execute){
                        log.debug("fullEntity: {}", JSONUtil.toJsonStr(fullEntity));
                        continue;
                    }else {
                        //Object value = BeanMap.create(fullEntity).get(StrUtil.toCamelCase(extractTable.foreignKey()));
                        //log.info("@ExtractTable[{} -> {}:{}={}]", entityClass.getSimpleName(), extractTable.value(), extractTable.foreignKey(), value);
                    }
                }
                //找到指向的主表
                TableInfo tableInfo = TableInfoHelper.getTableInfo(extractTable.value());
                Class<?> mainClass = tableInfo.getEntityType();
                TableName parent = mainClass.getAnnotation(TableName.class);

                String foreignKey = extractTable.foreignKey();
                Serializable mainKey = this.foreignKeyHandler.getMainKey(entity, extractTable.value(), foreignKey);
                Objects.requireNonNull(mainKey, String.format("## mainKey is null: %s, %s", extractTable.value(), foreignKey));
                String threadKey = parent.value()+":"+entityClass.getName();
                Object value = threadLocal.getValue(threadKey, mainKey);
                if(value==null){
                    //主表存在@ExtractKey或@ExtractCount或@ExtractSum时，设置到当前线程中
                    if(isExtractKey(mainClass) || isExtractCount(mainClass) || isExtractSum(mainClass)){
                        Object mainEntity = foreignKeyHandler.getMainEntity(entity, extractTable.value(), foreignKey);
                        if(mainEntity!=null){
                            //删除之前处理：取Entity对象并设置到当前线程中
                            threadLocal.set(threadKey, mainKey, mainEntity, child.value());
                            log.info("set @ExtractTable > {}={}", threadKey+":"+mainKey, mainEntity);
                        }else {
                            log.warn(String.format("## select %s by %s is null", extractTable.value(), foreignKey));
                        }
                    }else {
                        log.debug("no find @ExtractKey @ExtractCount @ExtractSum in {}", mainClass);
                    }
                }
            }
        }
    }

    private Object getEntityById(Object entity) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(entity.getClass());
        String currentNamespace = tableInfo.getCurrentNamespace();
        Class<BaseMapper> mainMapperClass = ClassUtil.loadClass(currentNamespace);
        BaseMapper mainMapper = SpringUtil.getBean(mainMapperClass);
        Serializable idValue = getIdValue(entity);
        Object newEntity = mainMapper.selectById(idValue);
        return newEntity;
    }

    /*private String getIdFieldName(Object entity){
        for(Field field : entity.getClass().getDeclaredFields()){
            TableId tableId = field.getAnnotation(TableId.class);
            if(tableId!=null){
                return field.getName();
            }
        }
        return "id";
    }*/

    private Serializable getIdValue(Object entity) {
        Serializable idValue = null;
        if (entity != null) {
            idValue = (Serializable) BeanMap.create(entity).get("id");
            if(idValue!=null){
                return idValue;
            }
        }
        Field[] declaredFields = entity.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            TableId tableId = field.getAnnotation(TableId.class);
            if (tableId != null) {
                idValue = (Serializable) BeanMap.create(entity).get(field.getName());
                break;
            }
        }
        return idValue;
    }

    public boolean isExtractCount(Class<?> clazz){
        Field[] declaredFields = clazz.getDeclaredFields();
        if(declaredFields!=null && declaredFields.length>0){
            for (Field field : declaredFields){
                ExtractCount[] extractCounts = field.getAnnotationsByType(ExtractCount.class);
                if(extractCounts!=null && extractCounts.length>0){
                    return true;
                }
            }
        }
        return false;
    }

    public boolean isExtractSum(Class<?> clazz){
        Field[] declaredFields = clazz.getDeclaredFields();
        if(declaredFields!=null && declaredFields.length>0){
            for (Field field : declaredFields){
                ExtractSum[] extractSums = field.getAnnotationsByType(ExtractSum.class);
                if(extractSums!=null && extractSums.length>0){
                    return true;
                }
            }
        }
        return false;
    }

    public boolean isExtractKey(Class<?> clazz){
        Field[] declaredFields = clazz.getDeclaredFields();
        if(declaredFields!=null && declaredFields.length>0){
            for (Field field : declaredFields){
                ExtractKey[] extractKeys = field.getAnnotationsByType(ExtractKey.class);
                if(extractKeys!=null && extractKeys.length>0){
                    return true;
                }
            }
        }
        return false;
    }
}
